E コマースインターフェース


Spread.Viewsのさまざまな拡張機能を使用すると、包括的なEコマースWebサイトのデザインレイアウトを作成できます。Spread.Viewsの最適化されたユーザーインタフェースは、大画面デバイスはもちろん、モバイル画面やタブレット画面でもサポートされます。

ここで紹介するデモは、Spread.Viewsの以下の機能を使用することで作成できます。

Spread.Viewsを使用して、次のイメージのようなEコマースWebサイトのインタフェースを作成するには、次の手順を実行します。

ecommgrid

サンプルコード

  1. 以下のCSSおよびJS参照をプロジェクトに追加します。
    <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/gc.spread.views.dataview.10.0.0.css">
    <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/bootstrap-snippet.min.css">
    <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/gc.spread.views.cardlayout.10.0.0.css" />
    <script src="[Your Script Path]/gc.spread.common.10.0.0.min.js" type="text/javascript"></script>
    <script src="[Your Script Path]/gc.spread.views.dataview.10.0.0.min.js" type="text/javascript"></script>
    <script src="[Your Script Path]/lodash.min.js" type="text/javascript"></script>
    <script src="[Your Script Path]/gc.spread.views.cardlayout.10.0.0.min.js" type="text/javascript"></script>
    <script src="[Your Script Path]/zepto.min.js" type="text/javascript"></script>
    <script src="[Your Script Path]/license.js" type="text/javascript"></script>
       <script src="data/TVData.js" type="text/javascript"></script>
  2. headタグ内に、インタフェースにスタイルを適用するためのstyleタグを追加します。

     * {
                -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
            }
    
            ::-webkit-scrollbar {
                width: 5px;
                height: 5px;
            }
    
            ::-webkit-scrollbar-thumb {
                background: #e0e0e0;
                border-radius: 5px;
            }
    
            label {
                font-weight: normal;
                margin: 0;
            }
    
            .card-layout.gc-grid {
                border: none;
            }
    
            .card-layout .gc-row {
                text-align: center;
                padding: 3px;
            }
    
            #grid1 {
                position: absolute;
                left: 220px;
                right: 0;
                height: 100%;
            }
    
            .demo-container {
                -moz-user-select: none;
                -khtml-user-select: none;
                -webkit-user-select: none;
                -ms-user-select: none;
                user-select: none;
                width: 100%;
                height: 100%;
                position: relative;
            }
    
            .filter-panel {
                float: left;
                width: 219px;
                height: 100%;
                overflow: auto;
                display: block;
                border: none;
                z-index: auto;
                position: static;
            }
    
            .filter-header {
                color: #999;
                font-size: 17px;
                padding-bottom: 10px;
                padding-top: 6px;
            }
    
            .filter-details {
                font-size: 14px;
                font-weight: 700;
                padding-bottom: 3px;
            }
    
            .stars-box {
                display: inline-block;
                height: 13px;
                width: 65px;
                overflow: hidden;
                background-image: url("./images/star-ratings.png");
                vertical-align: middle;
            }
    
            .stars-0 {
                background-position: -65px 0;
            }
    
            .stars-0-5 {
                background-position: -52px -19px;
            }
    
            .stars-1 {
                background-position: -52px 0;
            }
    
            .stars-1-5 {
                background-position: -39px -19px;
            }
    
            .stars-2 {
                background-position: -39px 0;
            }
    
            .stars-2-5 {
                background-position: -26px -19px;
            }
    
            .stars-3 {
                background-position: -26px 0;
            }
    
            .stars-3-5 {
                background-position: -13px -19px;
            }
    
            .stars-4 {
                background-position: -13px 0;
            }
    
            .stars-4-5 {
                background-position: 0 -18px;
            }
    
            .stars-5 {
                background-position: 0 0;
            }
    
            .tv-image {
                margin-left: 10px;
            }
    
            .tv-brand {
                font-size: 13px;
                color: lightgrey;
                margin-bottom: 8px;
            }
    
            .tv-price {
                color: #b12704;
            }
    
            .mobile-filter-entry {
                display: none;
            }
    
            .filter-condition {
                display: inline-block;
                border: 1px solid lightgrey;
                width: 100px;
                height: 25px;
                font-family: -apple-system-font, "Helvetica Neue", 'Segoe UI', Helvetica, Arial, sans-serif;
                font-size: 16px;
                text-align: center;
                cursor: pointer;
            }
    
            .slicer {
                margin: 10px 0;
            }
    
            .slicer-item {
                margin: 2px 0;
                cursor: pointer;
            }
    
            .hover {
                color: #E47911;
            }
    
            .filtered {
                background-color: #FFFFFF;
            }
    
            .filteredOutByOther {
                color: #A6A8B1;
            }
    
            .filteredOutBySelf {
                background-color: #FFFFFF;
            }
    
            @media only screen and (max-width: 768px) {
                #grid1 {
                    position: static;
                    height: 90%;
                }
                .filter-panel {
                    margin-top: -1px;
                    border: 1px solid lightgrey;
                    width: 100%;
                    overflow: auto;
                    height: 90%;
                    position: absolute;
                    z-index: 1;
                    background: white;
                    padding: 5px 0 0 10px;
                    display: none;
                }
                .mobile-filter-entry {
                    height: 25px;
                    display: block;
                }
        }
  3. bodyタグ内にdivタグを追加して、ページ内のコンテナとしてDOM要素を含めます。

    <div class="demo-container">
            <div class="mobile-filter-entry">
                <div class="filter-condition"><span style="padding-right:5px;">Filter</span><span class="fa fa-angle-down"></span></div>
            </div>
            <div class="filter-panel">
                <div class="filter-header">Refine by</div>
                <div id="tv_display_size" class="slicer"></div>
                <div id="tv_resolution" class="slicer"></div>
                <div id="customer_review_star" class="slicer"></div>
            </div>
            <div id="grid1"></div>
        </div>
        <script type="text/javascript">
            var slicerComponentNS = GC.Spread.Slicers;
            var rowTemplate =
                '<div>' +
                '<div data-column="image"></div>' +
                '<div data-column="description"></div>' +
                '<div data-column="brand"></div>' +
                '<div data-column="price"></div>' +
                '<div data-column="starsIcon"></div>' +
                '</div>';
    
            var imagePresenter = '<img class="tv-image" src={{=it.image}} />';
            var descriptionPresenter = '<a href="#"><b>{{=it.description}}</b></a>';
            var brandPresenter = '<div class="tv-brand"><label>by {{=it.brand}}</label></div>';
            var pricePresenter = '<div>${{=it.price}}</div>';
        var startPresenter = '<div class="stars-box {{=it.starsIcon}}"></div>';
  4. Spread.Viewsのインスタンスを作成するための変数定義を追加します。

    var dataView;
    var rowTemplate =
        '<div>' +
        '<div data-column="image"></div>' +
        '<div data-column="description"></div>' +
        '<div data-column="brand"></div>' +
        '<div data-column="price"></div>' +
        '<div data-column="starsIcon"></div>' +
        '</div>';
    
    var imagePresenter = '<img class="tv-image" src={{=it.image}} />';
    var descriptionPresenter = '<a href="#"><b>{{=it.description}}</b></a>';
    var brandPresenter = '<div class="tv-brand"><label>by {{=it.brand}}</label></div>';
    var pricePresenter = '<div>${{=it.price}}</div>';
    var startPresenter = '<div class="stars-box {{=it.starsIcon}}"></div>';
  5. グリッド値を指定する列定義を追加します。
     var columns = [{
                id: 'image',
                caption: 'Image',
                dataField: 'image',
                presenter: imagePresenter
            }, {
                id: 'description',
                caption: 'Description',
                dataField: 'description',
                presenter: descriptionPresenter
            }, {
                id: 'brand',
                caption: 'Brand',
                dataField: 'brand',
                presenter: brandPresenter
            }, {
                id: 'price',
                caption: 'Price',
                dataField: 'price',
                presenter: pricePresenter
            }, {
                id: 'starsIcon',
                caption: 'StarsIcon',
                dataField: 'starsIcon',
                presenter: startPresenter
            }, {
                id: 'size',
                caption: 'TV Display Size',
                dataField: 'size'
            }, {
                id: 'refreshRate',
                caption: 'RefreshRate',
                dataField: 'refreshRate'
            }, {
                id: 'resolution',
                caption: 'Television Resolution',
                dataField: 'resolution'
            }, {
                id: 'starsValue',
                caption: 'Avg. Customer Review',
                dataField: 'starsValue'
        }];
  6. DIVタグのグリッドIDを呼び出し、コードを初期化します。
     var dataView = new GC.Spread.Views.DataView(document.getElementById('grid1'), data, columns, new GC.Spread.Views.Plugins.CardLayout({
                cardHeight: 300,
                cardWidth: 210,
                rowTemplate: rowTemplate
        }));
  7. フィルタリング条件を設定する関数を指定します。

    var SlicerBase = (function() {
                var SlicerBase = function(container, slicerData, columnName) {
                    var self = this;
                    self.container = container;
                    self.slicerData = slicerData;
                    self.columnName = columnName;
                    self.exclusiveDatas = slicerData.getExclusiveData(columnName);
                    self.slicerData.attachListener(self);
                    self.onDataLoaded();
                }
    
                SlicerBase.prototype = {
                    onDataLoaded: function() {
                        var self = this;
                        var container = self.container;
                        var renderedHTML = self.getRenderedHTML();
                        container.append(renderedHTML);
    
                        container.find('.slicer-item input[type=checkbox]').on('click', function(e) {
                            e.preventDefault();
                        });
                        container.find('.slicer-item').on('mouseenter', function(e) {
                            $(this).addClass("hover");
                        }).on('mouseleave', function(e) {
                            $(this).removeClass("hover");
                        }).on('mousedown', {
                            slicer: this
                        }, function(e) {
                            var slicer = e.data.slicer;
                            var slicerItem = $(e.currentTarget);
                            var targetInput = slicerItem.find('input[type=checkbox]');
                            if (targetInput) {
                                targetInput.prop('checked', !targetInput.prop('checked'));
                            }
                            var condition = slicer.getFilterCondition(e, slicer);
                            if (!condition) {
                                slicer.slicerData.doUnfilter(slicer.columnName);
                            } else {
                                slicer.slicerData.doFilter(slicer.columnName, condition);
                            }
                        });
                    },
                    onFiltered: function(args) {
                        clearSlicerItemClass.call(this);
                        this.updateSlicerItem();
                        var newData = _.map(args.rowIndexes, function(index) {
                            return data[index];
                        });
                        dataView.data.setSource_(newData); //Need to be replaced
                        dataView.invalidate();
                    }
                };
    
                function clearSlicerItemClass() {
                    var items = this.container.find('.slicer-item');
                    var classes = ['filtered', 'filteredOutByOther'];
                    _.each(items, function(item) {
                        _.each(classes, function(itemClass) {
                            $(item).removeClass(itemClass);
                        });
                    });
                }
    
                return SlicerBase;
            })();
    
            var ResolutionFilter = (function(super_) {
                var ResolutionFilter = function(container, slicerData, columnName) {
                    super_.call(this, container, slicerData, columnName);
                }
                extends_(ResolutionFilter, super_);
    
                ResolutionFilter.prototype.getRenderedHTML = function() {
                    var self = this;
                    var columnName = self.columnName;
                    var datas = self.exclusiveDatas;
                    var slicerData = self.slicerData;
    
                    var header = '<div class="filter-details">' + getCaption(columnName) + '</div>';
                    var body = '';
                    for (var i = 0, length = datas.length; i < length; i++) {
                        var count = slicerData.getRowIndexes(columnName, i).length;
                        body += '<div class="slicer-item"><input type="checkbox" /><span>' + datas[i] + '</span><span style="color:#A29999">(' + count + ')</span></div>';
                    }
                    return header + body;
                };
    
                ResolutionFilter.prototype.getFilterCondition = function(e, slicer) {
                    var container = slicer.container;
                    var inputs = container.find('input[type=checkbox]');
                    var indexes = [];
                    _.each(inputs, function(item, i) {
                        if (item.checked) {
                            indexes.push(i);
                        }
                    });
                    return indexes.length ? {
                        exclusiveRowIndexes: indexes
                    } : null;
                };
    
                ResolutionFilter.prototype.updateSlicerItem = function() {
                    var self = this;
                    var items = this.container.find('.slicer-item');
                    var i;
                    var length;
                    var filteredItems = self.slicerData.getFilteredIndexes(self.columnName);
                    for (i = 0, length = filteredItems.length; i < length; i++) {
                        $(items[filteredItems[i]]).addClass("filtered");
                    }
                    var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns);
                    for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) {
                        $(items[filteredOutByOtherItems[i]]).addClass("filteredOutByOther");
                    }
                };
    
                return ResolutionFilter;
            })(SlicerBase);
    
            var DisplaySizeFilter = (function(super_) {
                var DisplaySizeFilter = function(container, slicerData, columnName) {
                    this.rangeInfo = [{
                        range: {
                            min: -Infinity,
                            max: 32
                        },
                        label: '32 Inches & Under'
                    }, {
                        range: {
                            min: 33,
                            max: 43
                        },
                        label: '33 to 43 Inches'
                    }, {
                        range: {
                            min: 44,
                            max: 49
                        },
                        label: '44 to 49 Inches'
                    }, {
                        range: {
                            min: 50,
                            max: 59
                        },
                        label: '50 to 59 Inches'
                    }, {
                        range: {
                            min: 60,
                            max: 69
                        },
                        label: '60 to 69 Inches'
                    }, {
                        range: {
                            min: 70,
                            max: Infinity
                        },
                        label: '70 Inches & Up'
                    }];
                    super_.call(this, container, slicerData, columnName);
                };
                extends_(DisplaySizeFilter, super_);
    
                DisplaySizeFilter.prototype.getRenderedHTML = function() {
                    var self = this;
                    var columnName = self.columnName;
                    var slicerData = self.slicerData;
    
                    var header = '<div class="filter-details">' + getCaption(columnName) + '</div>';
                    var body = '';
                    var info = self.rangeInfo;
                    for (var i = 0, length = info.length; i < length; i++) {
                        var count = slicerData.getData(columnName, info[i].range).length;
                        body += '<div class="slicer-item"><input type="checkbox" /><span>' + info[i].label + '</span><span style="color:#A29999">(' + count + ')</span></div>';
                    }
                    return header + body;
                };
    
                DisplaySizeFilter.prototype.getFilterCondition = function(e, slicer) {
                    var container = slicer.container;
                    var inputs = container.find('input[type=checkbox]');
                    var ranges = [];
                    _.each(inputs, function(item, i) {
                        if (item.checked) {
                            ranges.push(slicer.rangeInfo[i].range);
                        }
                    });
                    return ranges.length ? {
                        ranges: ranges
                    } : null;
                };
    
                DisplaySizeFilter.prototype.updateSlicerItem = function() {
                    var self = this;
                    var i;
                    var length;
                    var j;
                    var len;
                    var filteredItems = self.slicerData.getFilteredIndexes(self.columnName);
                    var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns);
                    var items = this.container.find('.slicer-item');
                    var slicerData = self.slicerData;
                    var columnIndex = slicerData.getColumnIndex(self.columnName);
                    for (j = 0, len = items.length; j < len; j++) {
                        var range = self.rangeInfo[j].range;
                        for (i = 0, length = filteredItems.length; i < length; i++) {
                            if (range.min <= slicerData.data[filteredItems[i]][columnIndex] && slicerData.data[filteredItems[i]][columnIndex] <= range.max) {
                                $(items[j]).addClass("filtered");
                            }
                        }
                        for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) {
                            if (range.min <= slicerData.data[filteredOutByOtherItems[i]][columnIndex] && slicerData.data[filteredOutByOtherItems[i]][columnIndex] <= range.max) {
                                $(items[j]).addClass("filteredOutByOther");
                            }
                        }
                    }
                };
    
                return DisplaySizeFilter;
            })(SlicerBase);
    
            var CustomerReviewFilter = (function(super_) {
                var CustomerReviewFilter = function(container, slicerData, columnName) {
                    this.rangeInfo = [{
                        range: {
                            min: 4,
                            max: Infinity
                        },
                        label: '<span class="stars-box stars-4"></span><span>&Up</span>'
                    }, {
                        range: {
                            min: 3,
                            max: Infinity
                        },
                        label: '<span class="stars-box stars-3"></span><span>&Up</span>'
                    }, {
                        range: {
                            min: 2,
                            max: Infinity
                        },
                        label: '<span class="stars-box stars-2"></span><span>&Up</span>'
                    }, {
                        range: {
                            min: 1,
                            max: Infinity
                        },
                        label: '<span class="stars-box stars-1"></span><span>&Up</span>'
                    }];
                    super_.call(this, container, slicerData, columnName);
                }
                extends_(CustomerReviewFilter, super_);
    
                CustomerReviewFilter.prototype.getRenderedHTML = function() {
                    var self = this;
                    var columnName = self.columnName;
                    var slicerData = self.slicerData;
    
                    var header = '<div class="filter-details">' + getCaption(columnName) + '</div>';
                    var body = '';
                    var info = self.rangeInfo;
                    for (var i = 0, length = info.length; i < length; i++) {
                        var count = slicerData.getData(columnName, info[i].range).length;
                        body += '<div data-index=' + i + ' class="slicer-item">' + info[i].label + '<span style="color:#A29999">(' + count + ')</span></div>';
                    }
                    return header + body;
                };
    
                CustomerReviewFilter.prototype.getFilterCondition = function(e, slicer) {
                    var currentTarget = e.currentTarget;
                    var index = $(currentTarget).data('index');
                    return {
                        ranges: [slicer.rangeInfo[index].range]
                    };
                };
    
                CustomerReviewFilter.prototype.updateSlicerItem = function() {
                    var self = this;
                    var i;
                    var length;
                    var j;
                    var len;
                    var filteredItems = self.slicerData.getFilteredIndexes(self.columnName);
                    var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns);
                    var items = this.container.find('.slicer-item');
                    var slicerData = self.slicerData;
                    var columnIndex = slicerData.getColumnIndex(self.columnName);
                    for (j = 0, len = items.length; j < len; j++) {
                        var range = self.rangeInfo[j].range;
                        for (i = 0, length = filteredItems.length; i < length; i++) {
                            if (range.min <= slicerData.data[filteredItems[i]][columnIndex] && slicerData.data[filteredItems[i]][columnIndex] <= range.max) {
                                $(items[j]).addClass("filtered");
                            }
                        }
                        for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) {
                            if (range.min <= slicerData.data[filteredOutByOtherItems[i]][columnIndex] && slicerData.data[filteredOutByOtherItems[i]][columnIndex] <= range.max) {
                                $(items[j]).addClass("filteredOutByOther");
                            }
                        }
                    }
                };
    
                return CustomerReviewFilter;
            })(SlicerBase);
    
            function getCaption(id) {
                var column = _.find(columns, function(col) {
                    return col.id === id;
                });
                return column.caption;
            }
    
            function extends_(d, b) {
                for (var p in b) {
                    if (b.hasOwnProperty(p)) {
                        d[p] = b[p];
                    }
                }
    
                function __() {
                    this.constructor = d;
                }
    
                __.prototype = b.prototype;
                d.prototype = new __();
            }
    
            var dataNames = _.keys(data[0]);
            var dataArr = [];
            _.forEach(data, function(item) {
                dataArr.push(_.values(item));
            });
            var dataSource = new slicerComponentNS.GeneralSlicerData(dataArr, dataNames);
            var TVDisplaySizeFilter = new DisplaySizeFilter($('#tv_display_size'), dataSource, 'size');
            var TVResolutionFilter = new ResolutionFilter($('#tv_resolution'), dataSource, 'resolution');
        var CustomerReviewFilter = new CustomerReviewFilter($("#customer_review_star"), dataSource, "starsValue");